package com.boot.dog.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.boot.dog.common.constant.SecurityConst;
import com.boot.dog.common.enums.UserType;
import com.boot.dog.common.util.ApiResult;
import com.boot.dog.common.util.Result;
import com.boot.dog.common.util.SecurityUtil;
import com.boot.dog.entities.pojo.Area;
import com.boot.dog.entities.pojo.Organization;
import com.boot.dog.entities.pojo.User;
import com.boot.dog.entities.pojo.UserArea;
import com.boot.dog.entities.vo.Area4ChildVo;
import com.boot.dog.mapper.AreaMapper;
import com.boot.dog.mapper.OrganizationMapper;
import com.boot.dog.mapper.UserAreaMapper;
import com.boot.dog.mapper.UserMapper;
import com.boot.dog.service.AreaService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Author: MaxLv
 * @Date: 2020/1/16
 */
@Service
@AllArgsConstructor
public class AreaServiceImpl implements AreaService {

    private final AreaMapper areaMapper;

    private final OrganizationMapper organizationMapper;

    private final UserMapper userMapper;

    private final UserAreaMapper userAreaMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ApiResult<Void> saveArea(Area area) {
        Integer count = areaMapper.selectCount(Wrappers.<Area>query().lambda().eq(Area::getTitle, area.getTitle()));
        if (count > 0) {
            return Result.warn("区域名称已存在");
        }
        areaMapper.insert(area);

        String areaPath = StrUtil.join("-", area.getAreaPath(), area.getId());
        if (SecurityConst.PROVINCE_LEVEL.equals(area.getLevel())) {
            areaPath = area.getId() + StringUtils.EMPTY;
        }
        area.setAreaPath(areaPath);
        areaMapper.updateById(area);

        Area parentArea = new Area();
        parentArea.setId(area.getParentId());
        parentArea.setLeaf(SecurityConst.AREA_NO_LEAF);
        areaMapper.updateById(parentArea);
        return Result.ok("添加区域成功");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ApiResult<Void> editArea(Area area) {
        Integer count = areaMapper.selectCount(Wrappers.<Area>query().lambda().eq(Area::getTitle, area.getTitle()).ne(Area::getId, area.getId()));
        if (count > 0) {
            return Result.warn("区域名称已存在");
        }
        areaMapper.updateById(area);
        return Result.ok("修改区域成功");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ApiResult<Void> delById(Integer id) {
        Integer orgCount = organizationMapper.selectCount(Wrappers.<Organization>query().lambda().eq(Organization::getAreaId, id));
        if (orgCount > 0) {
            return Result.warn("区域下存在机构");
        }
        Integer childCount = areaMapper.selectCount(Wrappers.<Area>query().lambda().eq(Area::getParentId, id));
        if (childCount > 0) {
            return Result.warn("存在子节点");
        }
        Integer userCount = userAreaMapper.selectCount(Wrappers.<UserArea>query().lambda().eq(UserArea::getStreetId, id));
        if (userCount > 0) {
            return Result.warn("区域下存在人员");
        }
        // TODO 需要验证企业是否拥有该区域
        areaMapper.deleteById(id);
        Area area = areaMapper.selectById(id);
        if (area.getParentId() != 0) {
            Area parentArea = new Area();
            parentArea.setId(area.getParentId());
            parentArea.setLeaf(SecurityConst.AREA_LEAF);
            areaMapper.updateById(parentArea);
        }
        return Result.ok("删除区域成功");
    }

    @Override
    public ApiResult<List<Area>> searchArea(String title) {
        Map<Integer, List<Integer>> map = this.currUserArea();
        if (map != null) {
            List<Area> areas = areaMapper.selectList(Wrappers.<Area>query().lambda().like(Area::getTitle, title).or().in(Area::getId, map.get(SecurityConst.PROVINCE_LEVEL)).or().in(Area::getId, map.get(SecurityConst.CITY_LEVEL)).or().in(Area::getId, map.get(SecurityConst.COUNTY_LEVEL)).or().in(Area::getId, map.get(SecurityConst.STREET_LEVEL)));
            return Result.of(areas);
        }
        List<Area> areas = areaMapper.selectList(Wrappers.<Area>query().lambda().like(Area::getTitle, title));
        return Result.of(areas);
    }

    @Override
    public Map<Integer, List<Integer>> currUserArea() {
        Map<Integer, List<Integer>> map = null;
        User user = userMapper.selectOne(Wrappers.<User>query().lambda().eq(User::getUsername, SecurityUtil.getUser().getUsername()));
        if (!UserType.SUPER_ADMIN.code().equals(user.getUserType())) {
            map = new HashMap<>(4);
            List<UserArea> userAreas = userAreaMapper.selectList(Wrappers.<UserArea>query().lambda().eq(UserArea::getUserId, user.getId()));
            map.put(SecurityConst.PROVINCE_LEVEL, userAreas.stream().map(UserArea::getProvinceId).distinct().collect(Collectors.toList()));
            map.put(SecurityConst.CITY_LEVEL, userAreas.stream().map(UserArea::getCityId).distinct().collect(Collectors.toList()));
            map.put(SecurityConst.COUNTY_LEVEL, userAreas.stream().map(UserArea::getCountyId).distinct().collect(Collectors.toList()));
            map.put(SecurityConst.STREET_LEVEL, userAreas.stream().map(UserArea::getStreetId).distinct().collect(Collectors.toList()));
        }
        return map;
    }


    @Override
    public ApiResult<List<Area>> getAreaByParentId(Integer parentId, Integer level) {
        List<Area> areas = getAreaByLevel(parentId, (level + 1));
        return Result.of(areas);
    }

    @Override
    public ApiResult<List<Area4ChildVo>> getArea4Search(Integer parentId, Integer level) {
        List<Area> areas = getAreaByLevel(parentId, (level + 1));
        if (CollUtil.isEmpty(areas)) {
            return Result.of(new ArrayList<>());
        }
        List<Area4ChildVo> vos = areas.stream().map(area -> {
            Area4ChildVo vo = new Area4ChildVo();
            vo.setValue(area.getId());
            vo.setLeaf(area.getLeaf());
            vo.setLevel(area.getLevel());
            vo.setParentId(area.getParentId());
            vo.setLabel(area.getTitle());
            return vo;
        }).collect(Collectors.toList());
        return Result.of(vos);
    }

    private List<Area> getAreaByLevel(Integer parentId, Integer level) {
        Map<Integer, List<Integer>> map = this.currUserArea();
        QueryWrapper<Area> queryWrapper = new QueryWrapper<>();
        if (parentId != null) {
            queryWrapper.lambda().eq(Area::getParentId, parentId);
        }
        if (level != null) {
            queryWrapper.lambda().eq(Area::getLevel, level);
        }
        if (map != null) {
            List<Integer> ids = map.get(level);
            queryWrapper.lambda().in(Area::getId, ids);
        }
        return areaMapper.selectList(queryWrapper);
    }

    @Override
    public ApiResult<List<Area>> getAllArea() {
        List<Area> proAreas = getAreaByLevel(null, SecurityConst.PROVINCE_LEVEL);
        List<Area> cityAreas = getAreaByLevel(null, SecurityConst.CITY_LEVEL);
        List<Area> countyAreas = getAreaByLevel(null, SecurityConst.COUNTY_LEVEL);
        List<Area> streetAreas = getAreaByLevel(null, SecurityConst.STREET_LEVEL);
        handlerAreas(streetAreas, countyAreas);
        handlerAreas(countyAreas, cityAreas);
        handlerAreas(cityAreas, proAreas);
        return Result.of(proAreas);
    }

    private void handlerAreas(List<Area> areas, List<Area> parentAreas) {
        if (CollUtil.isNotEmpty(areas)) {
            parentAreas.forEach(pArea -> {
                pArea.setExpand(true);
                List<Area> children = areas.stream().filter(area -> pArea.getId().equals(area.getParentId())).collect(Collectors.toList());
                children.forEach(c -> {
                    c.setExpand(true);
                });
                pArea.setChildren(children);
            });
        }
    }

}
